package com.starsoft.quant.strategy.fund;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.starsoft.frame.util.DateUtil;
import com.starsoft.frame.util.NumberUtil;
import com.starsoft.frame.util.StringUtil;
import com.starsoft.quant.bean.QuantDetail;
import com.starsoft.quant.executor.StrategyReport;
import com.starsoft.quant.strategy.StrategyImpl;
import com.starsoft.smdc.bean.SmdcFundNavPre;
import com.starsoft.smdc.bean.SmdcMarketDaily;
import com.starsoft.smdc.bean.SmdcSecurity;
import com.starsoft.smdc.util.SecurityType;
import com.starsoft.trade.bean.TradePosition;

import cn.hutool.core.util.StrUtil;

public class CloseFundStragegy extends StrategyImpl {
	private static final Logger logger = LoggerFactory.getLogger(CloseFundStragegy.class);

	private Map<String, SmdcMarketDaily> lastDayMarketMap;
	
	private double TIME_TO_CHANGE;
	
	private int VOLATILITY_DAYS;
	
	private double CALL_COST;
	
	Map<String, SmdcFundNavPre> fundNavPreMap = new HashMap<>();
	
	@Override
	public StrategyReport handle() {
		TIME_TO_CHANGE = Double.parseDouble(modelContext.getModelParam("TIME_TO_CHANGE"));
		VOLATILITY_DAYS = Integer.parseInt(modelContext.getModelParam("VOLATILITY_DAYS"));
		CALL_COST = Double.parseDouble(modelContext.getModelParam("CALL_COST"));
	
		List<SmdcSecurity> securityList = context.getSecurityList();
		loadSmdcData(securityList);

		List<QuantDetail> details = new ArrayList<>();
		for (SmdcSecurity security : securityList) {
			try {
				QuantDetail report = analysis(security);
				if (report != null) {
					details.add(report);
				}
			} catch (Exception e) {
				logger.error(security.getSecId() + " " + DateUtil.toString(context.getCurrentDate())
						+ ",error message:" + e.getMessage(), e);
				context.getRunLog().append(security.getSecId()).append(" ");
				context.getRunLog().append(security.getSecName()).append(" ");
				context.getRunLog().append(DateUtil.toString(context.getCurrentDate())).append(" ");
				context.getRunLog().append(e.getMessage()+" ").append(e.getClass().toString()).append("\n");
			}
		}
		StrategyReport report = createReport(details);
		return report;
	}
	
	private void loadSmdcData(List<SmdcSecurity> securityList){
		Map<String, SmdcSecurity> indexMap = new HashMap<>();
		for (SmdcSecurity security : securityList) {
			boolean isFund = SecurityType.isFund(security.getSecType());
			boolean isCloseFund = security.getFundTradetype().equals("CLOSE");
			if (!isFund || !isCloseFund) {
				throw new RuntimeException(security.getSecId()+"不满足策略条件，封闭基金");
			}
		}
		
		fundNavPreMap = context.getFundNavPreService().getFundPreNavOnTime(securityList, context.getCurrentDate());

		Date lastDay = context.getHolidayService().getNextBusinessDay(context.getCurrentDate(), -1);
		List<String> secIds = new ArrayList<>(indexMap.keySet());
		for (SmdcSecurity security : securityList) {
			secIds.add(security.getSecId());
		}
		lastDayMarketMap = context.getMarketDailyService().getMarketMap(secIds, lastDay);
	}

	private QuantDetail analysis(SmdcSecurity security) {
		String secId = security.getSecId();
		Double lastNav = lastDayMarketMap.get(secId).getSecNav();
		if(lastNav == null){
			throw new RuntimeException("净值为空");
		}
		Double manageFee = security.getFundManageFee();
		if(manageFee == null){
			throw new RuntimeException("缺少管理费设置");
		}
		double maturity = DateUtil.getYears(context.getCurrentDate(), security.getFundCloseMaturity());
		
		Double lastClose = lastDayMarketMap.get(secId).getClosePrice();
		double lastDiscount = (lastNav / lastClose - 1) * 100;
		Double lastDiscountYearly = null, lastDiscountYearlyWithFee = null, redeptionFee = null;
		
		SmdcFundNavPre navPre = fundNavPreMap.get(secId);
		Double todayNav = context.getMarket(secId).getSecNav();
		Double todayPreNav =  navPre!=null? navPre.getPreNav():null;
		Double usedTodayNav = todayNav!=null ? todayNav : todayPreNav;
		Double todayNavChange = null, todayDiscount=null, todayDiscountYearly =null, todayDiscountYearlyWithFee = null;
		
		if (usedTodayNav != null) {
			todayNavChange = (usedTodayNav/lastNav-1);
			todayDiscount = (usedTodayNav / context.getMarket(secId).getClosePrice() - 1) * 100;
		}
		
		if (maturity > 0) {
			redeptionFee = getRedemptionFee(security, maturity);
			double buyFee = context.getAcountStatus().getPosition(secId)!=null?0:CALL_COST;
			lastDiscountYearly = (Math.pow(lastNav / lastClose, 1 / maturity)-1)*100;
			lastDiscountYearlyWithFee =lastDiscountYearly - (redeptionFee+buyFee) / maturity - manageFee;
			if (usedTodayNav != null) {
				todayDiscountYearly = Math.pow(usedTodayNav/context.getMarket(secId).getClosePrice(),1/maturity);
				todayDiscountYearly = (todayDiscountYearly-1)*100;
				todayDiscountYearlyWithFee = todayDiscountYearly - (redeptionFee+buyFee) / maturity - manageFee;;
			}
		}
		
		double volatility = context.getVolatilityService().getVolatilityNav(security, VOLATILITY_DAYS,
				DateUtil.getLast(context.getCurrentDate()));
		
		QuantDetail report = createDetail(security, NumberUtil.null2Zero(todayDiscountYearlyWithFee));
		report.setCol1(StringUtil.toStrNull(lastNav));
		report.setCol3(StringUtil.toStrNull(lastDiscount));
		report.setCol4(StringUtil.toStrNull(lastDiscountYearly));
		
		report.setCol5(DateUtil.toString(security.getFundCloseMaturity()));
		report.setCol6(StringUtil.toStrNull(maturity*12));
		report.setCol7(StringUtil.toStrNull(context.getMarket(secId).getTurnoverValue()));
		report.setCol8(StringUtil.toStrNull(volatility));
		report.setCol9(StringUtil.toStrNull(manageFee));
		
		TradePosition position = context.getAcountStatus().getPosition(security.getSecId());
		if(position!=null && position.getBuyDate()!=null) {
			report.setCol10(DateUtil.toString(position.getBuyDate()));
		}
		report.setCol11(StringUtil.toStrNull(todayNavChange));
		report.setCol12(StringUtil.toStrNull(todayNav));
		report.setCol13(StringUtil.toStrNull(todayDiscount));
		report.setCol14(StringUtil.toStrNull(todayDiscountYearly));
		report.setCol15(StringUtil.toStrNull(todayPreNav));

		report.setCol16(StringUtil.toStrNull(lastDiscountYearlyWithFee));
		report.setCol17(StringUtil.toStrNull(todayDiscountYearlyWithFee));
		report.setCol18(security.getRedemptionFee());
		report.setCol19(StringUtil.toStrNull(redeptionFee));
		
		return report;
	}

	private Double getRedemptionFee(SmdcSecurity security, 	double maturity) {
		try {
			if(StrUtil.isEmptyIfStr(security.getRedemptionFee())) return 1.5;
			double holdtime = maturity;
			TradePosition position = context.getAcountStatus().getPosition(security.getSecId());
			if(position!=null && position.getBuyDate()!=null) {
				maturity+=DateUtil.getYears(position.getBuyDate(), new Date());
			}

			List<String> fees = StrUtil.splitTrim(security.getRedemptionFee()," ");
			for(int i= fees.size()-1;i>=0;i--) {
				String[] feeDetail = fees.get(i).split(":");
				String timeStr = feeDetail[0].replace("天", "").replace("日", "").replace("月", "").replace("个", "").replace("年", "");
				Double timeDouble = Double.parseDouble(timeStr);
				if(feeDetail[0].contains("天")||feeDetail[0].contains("日")) {
					timeDouble = timeDouble/365;
				}else if(feeDetail[0].contains("月")||feeDetail[0].contains("个")) {
					timeDouble = timeDouble/12;
				}
				if(holdtime>timeDouble) {
					return Double.parseDouble(feeDetail[1]);
				}
			}
			return 1.5;
		} catch (Exception e) {
			e.printStackTrace();
			return 0D;
		}

	}

	
}
